"use strict";

var $ = require("jquery"),
    MouseAndTouchStrategy = require("events/pointer/mouse_and_touch"),
    registerEvent = require("events/core/event_registrator"),
    nativePointerMock = require("../../../helpers/nativePointerMock.js"),
    $eventSpecial = $.event.special;

QUnit.module("mouse and touch events", {
    beforeEach: function() {
        this.clock = sinon.useFakeTimers();

        $.each(MouseAndTouchStrategy.map, function(pointerEvent, originalEvents) {
            if($eventSpecial[pointerEvent]) {
                $eventSpecial[pointerEvent].dispose();
            }
            registerEvent(pointerEvent, new MouseAndTouchStrategy(pointerEvent, originalEvents));
        });

        this.$element = $("#element");
    },

    afterEach: function() {
        MouseAndTouchStrategy.resetObserver();
        this.clock.restore();
    }
});

QUnit.test("pointer event should not trigger twice on real devices", function(assert) {
    var handlerTriggerCount = 0;

    var $container = $("#container").on("dxpointerdown", function() {
        handlerTriggerCount++;
    });

    var $element = this.$element.on("dxpointerdown", function() {
        handlerTriggerCount++;
    });

    nativePointerMock($element)
        .start()
        .down();

    assert.equal(handlerTriggerCount, 2);

    nativePointerMock($container)
        .start()
        .down();

    assert.equal(handlerTriggerCount, 3);
});

QUnit.test("one pointer event should not unsubscribe another events", function(assert) {
    assert.expect(1);

    var $element = this.$element;

    $element
        .on("dxpointerdown dxpointerup", function(e) {
            assert.equal(e.type, "dxpointerup");
        })
        .off("dxpointerdown");

    $element.trigger("dxpointerdown");
    $element.trigger("dxpointerup");
});

QUnit.test("dxpointerup triggers twice on real devices", function(assert) {
    var triggered = 0;

    this.$element.on("dxpointerup", function() {
        triggered++;
    });

    var pointer = nativePointerMock(this.$element)
        .start()
        .touchStart()
        .touchEnd();

    this.clock.tick(300);

    pointer
        .mouseDown()
        .mouseUp();

    assert.equal(triggered, 1);
});


$.each({
    "dxpointerdown": "mouseDown",
    "dxpointermove": "mouseMove",
    "dxpointerup": "mouseUp",
    "dxpointerover": "mouseOver",
    "dxpointerout": "mouseOut",
    "dxpointerenter": "mouseEnter",
    "dxpointerleave": "mouseLeave"
}, function(eventName, triggerMethod) {
    QUnit.test("'" + eventName + "' has pointerType = 'mouse' if it was triggered by mouse event", function(assert) {
        this.$element.on(eventName, function(e) {
            assert.equal(e.pointerType, "mouse");
        });

        nativePointerMock(this.$element)[triggerMethod]();
    });
});

$.each({
    "dxpointerdown": "mouseDown",
    "dxpointermove": "mouseMove",
    "dxpointerup": "mouseUp",
    "dxpointerover": "mouseOver",
    "dxpointerout": "mouseOut",
    "dxpointerenter": "mouseEnter",
    "dxpointerleave": "mouseLeave"
}, function(eventName, triggerMethod) {
    QUnit.test("'" + eventName + "' event should have correct pointerId", function(assert) {
        this.$element.on(eventName, function(e) {
            assert.equal(e.pointerId, 1);
        });

        nativePointerMock(this.$element)[triggerMethod]();
    });
});

if(document.addEventListener) {
    var simulateMouseEvent = function($element, type, options) {
        options = $.extend({
            canBubble: true,
            cancelable: true,
            type: type
        }, options);

        var event = document.createEvent("MouseEvents");

        var args = [];
        $.each(["type", "canBubble", "cancelable", "view", "detail", "screenX", "screenY", "clientX", "clientY", "ctrlKey", "altKey",
            "shiftKey", "metaKey", "button", "relatedTarget"], function(i, name) {
            if(name in options) {
                args.push(options[name]);
            } else {
                args.push(event[name]);
            }
        });
        event.initMouseEvent.apply(event, args);

        $element[0].dispatchEvent(event);
    };

    QUnit.test("dxpointer events should have correct pointers", function(assert) {
        this.$element.one("dxpointerdown", function(e) {
            var pointers = e.pointers;
            assert.equal(pointers.length, 1);
        });
        simulateMouseEvent(this.$element, "mousedown");

        this.$element.one("dxpointermove", function(e) {
            var pointers = e.pointers;
            assert.equal(pointers.length, 1);
        });
        simulateMouseEvent(this.$element, "mousemove");

        this.$element.one("dxpointerup", function(e) {
            var pointers = e.pointers;
            assert.equal(pointers.length, 0);
        });
        simulateMouseEvent(this.$element, "mouseup");

        this.$element.one("dxpointermove", function(e) {
            var pointers = e.pointers;
            assert.equal(pointers.length, 0);
        });
        simulateMouseEvent(this.$element, "mousemove");
    });

    QUnit.test("pointers in dxpointer events should be updated on mouse move", function(assert) {
        simulateMouseEvent(this.$element, "mousedown", { clientX: 0 });

        this.$element.one("dxpointermove", function(e) {
            var pointers = e.pointers;

            assert.equal(pointers[0].pageX || pointers[0].clientX, 50);
        });
        simulateMouseEvent(this.$element, "mousemove", { clientX: 50 });
    });
}


$.each({
    "dxpointerdown": "touchstart",
    "dxpointermove": "touchmove",
    "dxpointerup": "touchend"
}, function(eventName, triggerEvent) {
    QUnit.test("'" + eventName + "' has pointerType = 'touch' if it was triggered by touch event", function(assert) {
        this.$element.on(eventName, function(e) {
            assert.equal(e.pointerType, "touch");
        });

        this.$element.trigger({
            type: triggerEvent,
            changedTouches: [{ identifier: 17 }],
            touches: [{ identifier: 17 }]
        });
    });
});

$.each({
    "dxpointerdown": "touchstart",
    "dxpointermove": "touchmove",
    "dxpointerup": "touchend"
}, function(eventName, triggerEvent) {
    QUnit.test("'" + eventName + "' event should have correct pointerId", function(assert) {
        this.$element.on(eventName, function(e) {
            assert.equal(e.pointerId, 17);
        });

        this.$element.trigger({
            type: triggerEvent,
            changedTouches: [{ identifier: 17 }],
            touches: [{ identifier: 17 }]
        });
    });
});

$.each({
    "dxpointerdown": "touchstart",
    "dxpointermove": "touchmove",
    "dxpointerup": "touchend"
}, function(eventName, triggerEvent) {
    QUnit.test("'" + eventName + "' event should have correct pointerId", function(assert) {
        this.$element.on(eventName, function(e) {
            if(eventName !== "dxpointerup") {
                assert.equal(e.pointers.length, 1, "pointer was added");
                var pointer = e.pointers[0];

                $.each(["pageX", "pageY"], function() {
                    assert.ok(this in pointer, "event has '" + this + "' property");
                });

                assert.equal(pointer.pointerId, 17);
            } else {
                assert.equal(e.pointers.length, 0, "pointers was cleaned");
            }
        });

        this.$element.trigger({
            type: triggerEvent,
            changedTouches: [{ identifier: 17, pageX: 0, pageY: 0 }],
            touches: eventName === "dxpointerup" ? [] : [{ identifier: 17, pageX: 0, pageY: 0 }]
        });
    });
});

$.each({
    "dxpointerdown": "touchstart",
    "dxpointermove": "touchmove",
    "dxpointerup": "touchend"
}, function(eventName, triggerEvent) {
    QUnit.test("'" + eventName + "' event should have correct pageX/pageY", function(assert) {
        this.$element.on(eventName, function(e) {
            assert.equal(e.pageX, 10);
            assert.equal(e.pageY, 15);
        });

        this.$element.trigger({
            type: triggerEvent,
            changedTouches: [{ identifier: 17, pageX: 10, pageY: 15 }],
            touches: eventName === "dxpointerup" ? [] : [{ identifier: 17, pageX: 10, pageY: 15 }]
        });
    });
});
